import subprocess
import platform

import nmcli
from sqlalchemy import select
from apscheduler.schedulers.background import BackgroundScheduler
from core.database import getDB
from schemas.network import NetWorkInfo, UsableWiFiInfo
from models.base import SystemInfoTable


global watchDogJob
watchDogJob = None


class Gateway:
    
    @property
    def id(self):
        with getDB() as db:
            return db.scalars(select(SystemInfoTable.id)).one()

    @property
    def name(self):
        with getDB() as db:
            return db.scalars(select(SystemInfoTable.name)).one()

    @property
    def position(self):
        with getDB() as db:
            return db.scalars(select(SystemInfoTable.position)).one()

    @property
    def version(self):
        with getDB() as db:
            return db.scalars(select(SystemInfoTable.version)).one()

    def getAddress(self, ifname):
        if platform.system() != "Linux":
            return None
        try:
            if ifname == "wifi":
                networks = nmcli.device()
                for nw in networks:
                    if nw.device_type == "wifi":
                        info = nmcli.device.show(nw.device)
                        _addr = info.get("IP4.ADDRESS[1]")
                        if _addr:
                            return _addr.split("/")[0]
            elif ifname == "wan":
                info = nmcli.device.show("eth0")
                _addr = info.get("IP4.ADDRESS[1]")
                if _addr:
                    return _addr.split("/")[0]
            elif ifname == "lan":
                info = nmcli.device.show("lan0")
                _addr = info.get("IP4.ADDRESS[1]")
                if _addr:
                    return _addr.split("/")[0]
        except Exception as e:
            return None

def getAddressAndNetmask(s: str | None):
    if not s or platform.system() != "Linux":
        return {
            "address": None,
            "netmask": None
		}
    addr, _mask = s.split("/")
    maskBits = int(_mask)
    oneNum = maskBits // 8
    last = maskBits % 8
    zeroNum = 3 - oneNum
    mask1 = ".".join(["255"] * oneNum)
    mask2 = "".join("1" * last) + "".join("0" * (8 - last))
    mask2 = str(int(mask2, 2))
    mask3 = ".".join(["0"] * zeroNum)
    mask = mask1 + "." + mask2
    if mask3:
        mask = mask + "." + mask3
    return {
        "address": addr,
        "netmask": mask
	}


def getConnectionInfo(connection):
    try:
        res = nmcli.connection.show(connection)
        return {
            "method": res.get("ipv4.method"),
            "gateway": res.get("ipv4.gateway"),
            "dns": res.get("ipv4.dns")
        }
    except Exception as e:
        print(e)
        return {
            "method": None,
            "gateway": None,
            "dns": None
        }


def getNetworkInfo():
    if platform.system() != "Linux":
        return []
    devs = [nmcli.device.show(dev.device) for dev in nmcli.device() if dev.device_type in ("wifi", "ethernet")]
    info = [NetWorkInfo(
        name=dev.get("GENERAL.CONNECTION"),
        device=dev.get("GENERAL.DEVICE"),
        type=dev.get("GENERAL.TYPE"),
        state=dev.get("GENERAL.STATE"),
        **getConnectionInfo(dev.get("GENERAL.CONNECTION")),
        **getAddressAndNetmask(dev.get("IP4.ADDRESS[1]")),
    ) if dev.get("GENERAL.TYPE") == "ethernet" else NetWorkInfo(
        name=dev.get("GENERAL.CONNECTION"),
        device=dev.get("GENERAL.DEVICE"),
        type=dev.get("GENERAL.TYPE"),
        state=dev.get("GENERAL.STATE"),
        method=getConnectionInfo(dev.get("GENERAL.CONNECTION"))["method"],
        gateway=dev.get("IP4.GATEWAY"),
        dns=dev.get("IP4.DNS[1]"),
        **getAddressAndNetmask(dev.get("IP4.ADDRESS[1]"))
    ) for dev in devs]
    return info


def updateNetworkInfo(name, options):
    if platform.system() != "Linux":
        return []
    nmcli.connection.modify(name, options)
    nmcli.connection.reload()


def getWiFi():
    return [UsableWiFiInfo(
        ssid=wifi.ssid,
        used=wifi.in_use,
        chan=wifi.chan,
        rate=wifi.rate,
        signal=wifi.signal,
        security=wifi.security
    ) for wifi in nmcli.device.wifi()]


def connectWiFi(ssid, password):
    try:
        nmcli.device.wifi_connect(ssid, password)
        return True
    except Exception as e:
        print(e)
        return False
    

def rescanWiFi():
    nmcli.device.wifi_rescan()


def watchDogTask(ip):
    proc = subprocess.run(
        ["ping", "-c", "3", "-s", "64", ip],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
    if proc.returncode != 0:
        subprocess.run("reboot", shell=True)


def startWatchDog(timer: BackgroundScheduler):
    if platform.system() != "Linux":
        return
    global watchDogJob
    with getDB() as db:
        result = db.scalars(select(SystemInfoTable)).one_or_none()
        if not result:
            result = SystemInfoTable(
                id="GATEWAY",
                name="LIoT边缘计算网关",
                position=None,
                version="0.1.0",
                watchEnable=False,
                watchIp=None,
                watchFreq=None
            )
            db.add(result)
            db.commit()
        freq = 60
        if result.watchFreq and result.watchFreq > 60:
            freq = result.watchFreq
        if result.watchEnable and result.watchIp:
            if watchDogJob:
                watchDogJob.remove()
            watchDogJob = timer.add_job(
                watchDogTask, "interval", 
                args=(result.watchIp,), 
                seconds=freq
            )
